package com.xquant.trade.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.xquant.common.core.constant.DictConstant;
import com.xquant.common.core.constant.SummitTranConstants;
import com.xquant.common.core.exception.ServiceException;
import com.xquant.common.core.utils.SummitMsgBuilder;
import com.xquant.common.engine.xPP.request.CalcInstrumentForm;
import com.xquant.common.log.annotation.ImportLog;
import com.xquant.common.log.enums.ImportActionType;
import com.xquant.common.log.enums.ImportOptionType;
import com.xquant.trade.account.entity.Account;
import com.xquant.trade.account.entity.Counterparty;
import com.xquant.trade.account.mapper.AccountMapper;
import com.xquant.trade.account.mapper.CounterpartyMapper;
import com.xquant.trade.systerm.mapper.InstrumentMapper;
import com.xquant.trade.trade.entity.Trade;
import com.xquant.trade.trade.mapper.TradeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * @author cloud
 * @version 1.0
 * @description: 校验交易对象业务规则的工具
 * @date 2022/4/22 17:30
 */
@Component
public class TradeValidatedUtil{

    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private CounterpartyMapper counterpartyMapper;

    @Autowired
    private TradeMapper tradeMapper;

    @Autowired
    private InstrumentMapper instrumentMapper;

    @ImportLog(businNoMethod = "getiCode")
    public Map validatedOtcTrade(Trade trade){
        return validatedTrade(trade);
    }

    @ImportLog(businNoMethod = "getiCode",actionType = ImportActionType.SUMMIT, unique = true, optionType = ImportOptionType.HAND)
    public Map validatedTradeByHandler(Trade trade){
        return validatedTrade(trade);
    }
    @ImportLog(businNoMethod = "getiCode",actionType = ImportActionType.SUMMIT, unique = true, optionType = ImportOptionType.AUTO, userId = "1")
    public Map validatedTradeByAuto(Trade trade){
        return validatedTrade(trade);
    }

//    @ImportLog(businNoMethod = "getiCode",actionType = ImportActionType.SUMMIT, unique = true)
    public Map validatedTrade(Trade trade){
        Map resultMap = new HashMap();
        resultMap.put("OK",true);
        if(trade.getAccId() == null || trade.getAccId().isEmpty()) {
            Account account = accountMapper.checkAccCode(trade.getAccName());
            if (account == null) {
                account = accountMapper.checkAccName(trade.getAccName());
                if (account == null) {
                    throw new ServiceException(SummitMsgBuilder.BuilderMsg(SummitTranConstants.ACCOUNT_EMTRY_ERR,trade.getAccName()));
//                    resultMap.put("OK", false);
//                    resultMap.put("msg", "系统内本方账户" + trade.getAccName() + "不存在");
//                    return resultMap;
                }
            }
            if (account.getStatus() != 1) {
                throw new ServiceException(SummitMsgBuilder.BuilderMsg(SummitTranConstants.ACCOUNT_AVAILABLE_ERR,trade.getAccName()));

//                resultMap.put("OK", false);
//                resultMap.put("msg", "系统内本方账户" + trade.getAccName() + "状态不为启用");
//                return resultMap;
            }
            trade.setAccId(String.valueOf(account.getAccId()));
        }
        if(trade.getPartyId() == null || trade.getPartyId().isEmpty()) {
            //校验对手账户是否存在
            Counterparty counterparty = counterpartyMapper.checkPartyName(trade.getPartyName());
            if (counterparty == null) {
                throw new ServiceException(SummitMsgBuilder.BuilderMsg(SummitTranConstants.CPACCOUNT_EMTRY_ERR,trade.getPartyName()));

//                resultMap.put("OK", false);
//                resultMap.put("msg", "系统内交易对手" + trade.getPartyName() + "不存在");
//                return resultMap;
            }
            if (counterparty.getStatus() != 1) {
                throw new ServiceException(SummitMsgBuilder.BuilderMsg(SummitTranConstants.CPACCOUNT_AVAILABLE_ERR,trade.getPartyName()));

//                resultMap.put("OK", false);
//                resultMap.put("msg", "系统内交易对手" + trade.getPartyName() + "状态不为启用");
//                return resultMap;
            }
            trade.setPartyId(String.valueOf(counterparty.getPartyId()));
        }
//        //校验合约编号是否已存在
//        Boolean iCodeFlag = checkICodeExist(trade);
//        if(iCodeFlag == false){
//            resultMap.put("OK",false);
//            resultMap.put("msg","系统内合约编号" + trade.getiCode() + "已存在");
//            return resultMap;
//        }
        //对标的进行校验
        HashMap<String, Object> iCodeMap = new HashMap<>();
        iCodeMap.put("iCode",trade.getInstrument().split(",")[0]);
        List<Map<String, Object>> instrument = instrumentMapper.getInstruments(iCodeMap);
        if (instrument == null){
            resultMap.put("OK",false);
            resultMap.put("msg","系统内标的代码" + trade.getiCode() + "不存在");
            return resultMap;
        }

        /**上面做一些通用的校验规则，下面根据期权类型做相应的校验*/
        String productType = trade.getCalcInstrumentForm().getProductType();
        switch (productType){
            case "Digital":
                resultMap = validDigital(trade);
                break;
            case "DoubleDigital":
                resultMap = validDoubleDigital(trade);
                break;
            case "SingleSharkFin":
                resultMap = validShark(trade);
                break;
            case "DoubleShark":
                resultMap = validDoubleShark(trade);
                break;
        }

        if(!resultMap.containsKey("OK")){
            resultMap.put("OK",true);       //  如果没有，就直接返回成功
        }
        return resultMap;
    }

    public Map validDigital(Trade trade){
        Map resultMap = new HashMap();
        resultMap.put("OK",true);

        CalcInstrumentForm calcInstrumentForm = trade.getCalcInstrumentForm();

        BigDecimal strike = new BigDecimal(calcInstrumentForm.getStrike());
        BigDecimal strikePercentage = new BigDecimal(calcInstrumentForm.getStrikePercentage());

        BigDecimal paymentAmount =calcInstrumentForm.getPaymentAmount();
        BigDecimal paymentAmountPecentage = calcInstrumentForm.getPaymentAmountPecentage();

        //对行权价进行校验
        if(strike.compareTo(BigDecimal.ZERO) == 0 && strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            throw new ServiceException(SummitTranConstants.SPOT_PRICE_ERR);
//            resultMap.put("OK",false);
//            resultMap.put("msg","行权价和行权比例不能都为空或零");
//            return resultMap;
        }else if(strike.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价为0，则取行权价比例设置行权价
            strikePercentage = strikePercentage.divide(BigDecimal.valueOf(100), 4, BigDecimal.ROUND_HALF_UP);
            strike = trade.getPrice().multiply(strikePercentage).setScale(2, BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setStrike(String.valueOf(strike));

        }else if(strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价比例为0，则取行权价/期初价格为行权价比例
            BigDecimal price = trade.getPrice();
            if (price.compareTo(BigDecimal.ZERO) == 0) {
                strikePercentage = new BigDecimal(BigInteger.ZERO);
            } else {
                strikePercentage =  strike.divide(trade.getPrice(),4,BigDecimal.ROUND_HALF_UP);
                strikePercentage = strikePercentage.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            }
            calcInstrumentForm.setStrikePercentage(String.valueOf(strikePercentage));
        }

        //对行权收益进行校验
        if(paymentAmount.compareTo(BigDecimal.ZERO) == 0 && paymentAmountPecentage.compareTo(BigDecimal.ZERO) == 0){
            throw new ServiceException(SummitTranConstants.PAYMENT_AMOUNT_ZERO_ERR);

//            resultMap.put("OK",false);
//            resultMap.put("msg","行权收益和收益金额不能都为空或零");
//            return resultMap;
        }else if(paymentAmount.compareTo(BigDecimal.ZERO) == 0){
            //没有收益金额，则根据行权收益和默认年化得到收益金额
            calcInstrumentForm.setMoneynessOption("0");
            BigDecimal annualRatio = getAnnualRatio(trade.getCalcDate(), trade.getIssueDate(), trade.getMaturityDate(), calcInstrumentForm.getDaycounter());
            paymentAmount = paymentAmountPecentage.divide(BigDecimal.valueOf(100)).multiply(trade.getVolume().multiply(annualRatio));
            calcInstrumentForm.setPaymentAmount(paymentAmount);

        }else if(paymentAmountPecentage.compareTo(BigDecimal.ZERO) == 0){
            //有收益金额，默认不年化
            calcInstrumentForm.setMoneynessOption("0");
            paymentAmountPecentage = paymentAmount.multiply(BigDecimal.valueOf(100)).divide(trade.getVolume(),2,BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setPaymentAmountPecentage(paymentAmountPecentage);
        }



        return resultMap;
    }

    public Map validDoubleDigital(Trade trade){
        Map resultMap = new HashMap();
        resultMap.put("OK",true);

        CalcInstrumentForm calcInstrumentForm = trade.getCalcInstrumentForm();

        BigDecimal strike = new BigDecimal(calcInstrumentForm.getStrike());
        BigDecimal strikePercentage = new BigDecimal(calcInstrumentForm.getStrikePercentage());

        BigDecimal paymentAmount = calcInstrumentForm.getPaymentAmount();
        BigDecimal paymentAmountPecentage = calcInstrumentForm.getPaymentAmountPecentage();

        //对行权价进行校验
        if(strike.compareTo(BigDecimal.ZERO) == 0 && strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            resultMap.put("OK",false);
            resultMap.put("msg","行权价和行权比例不能都为空或零");
            return resultMap;
        }else if(strike.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价为0，则取行权价比例设置行权价
            strikePercentage = strikePercentage.divide(BigDecimal.valueOf(100), 4, BigDecimal.ROUND_HALF_UP);
            strike = trade.getPrice().multiply(strikePercentage).setScale(2, BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setStrike(String.valueOf(strike));

        }else if(strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价比例为0，则取行权价/期初价格为行权价比例
            strikePercentage =  strike.divide(trade.getPrice(),4,BigDecimal.ROUND_HALF_UP);
            strikePercentage = strikePercentage.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setStrikePercentage(String.valueOf(strikePercentage));
        }

        //对行权收益进行校验
        if(paymentAmount.compareTo(BigDecimal.ZERO) == 0 && paymentAmountPecentage.compareTo(BigDecimal.ZERO) == 0){
            resultMap.put("OK",false);
            resultMap.put("msg","行权收益和收益金额不能都为空或零");
            return resultMap;
        }else if(paymentAmount.compareTo(BigDecimal.ZERO) == 0){
            //没有收益金额，则根据行权收益和默认年化得到收益金额
            calcInstrumentForm.setMoneynessOption("1");
            BigDecimal annualRatio = getAnnualRatio(trade.getCalcDate(), trade.getIssueDate(), trade.getMaturityDate(), calcInstrumentForm.getDaycounter());
            paymentAmount = paymentAmountPecentage.divide(BigDecimal.valueOf(100)).multiply(trade.getVolume().multiply(annualRatio));
            calcInstrumentForm.setPaymentAmount(paymentAmount);

        }else if(paymentAmountPecentage.compareTo(BigDecimal.ZERO) == 0){
            //有收益金额，默认不年化
            calcInstrumentForm.setMoneynessOption("0");
            paymentAmountPecentage = paymentAmount.multiply(BigDecimal.valueOf(100)).divide(trade.getVolume(),2,BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setPaymentAmountPecentage(paymentAmountPecentage);
        }
        return resultMap;
    }

    public Map validShark(Trade trade){
        Map resultMap = new HashMap();
        resultMap.put("OK",true);

        CalcInstrumentForm calcInstrumentForm = trade.getCalcInstrumentForm();

        BigDecimal strike = new BigDecimal(calcInstrumentForm.getStrike());
        BigDecimal strikePercentage = new BigDecimal(calcInstrumentForm.getStrikePercentage());


        //对行权价进行校验
        if(strike.compareTo(BigDecimal.ZERO) == 0 && strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            throw new ServiceException(SummitTranConstants.SPOT_PRICE_ERR);
//            resultMap.put("OK",false);
//            resultMap.put("msg",trade.getiCode()+"行权价和行权比例不能都为空或零");
//            return resultMap;
        }else if(strike.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价为0，则取行权价比例设置行权价
            strikePercentage = strikePercentage.divide(BigDecimal.valueOf(100), 4, BigDecimal.ROUND_HALF_UP);
            strike = trade.getPrice().multiply(strikePercentage).setScale(2, BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setStrike(String.valueOf(strike));

        }else if(strikePercentage.compareTo(BigDecimal.ZERO) == 0){
            //如果行权价比例为0，则取行权价/期初价格为行权价比例
            strikePercentage =  strike.divide(trade.getPrice(),4,BigDecimal.ROUND_HALF_UP);
            strikePercentage = strikePercentage.multiply(BigDecimal.valueOf(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            calcInstrumentForm.setStrikePercentage(String.valueOf(strikePercentage));
        }
//        if (form.data.exerciseType == 'American') {
//            if (form.data.minYieldRate > 0) {
//                if (Number(form.data.minYieldRate) >= Number(form.data.rebateRate)) {
//                    proxy.$modal.alertWarning('保底收益不能大于补偿收益,请核对!')
//                    return
//                }
//            }
//        }
        //看涨时障碍价必须大于行权价
        if (DictConstant.OPTION_DIRECT.CALL.equals(calcInstrumentForm.getOptionType()) &&calcInstrumentForm.getTriggerRate().compareTo(new BigDecimal(calcInstrumentForm.getStrikePercentage()))==-1) {
            throw new ServiceException(SummitTranConstants.TRIGGER_GREATE_ERR);
//            resultMap.put("OK",false);
//            resultMap.put("msg",trade.getiCode()+"障碍价必须大于行权价,请核对!");
//            return resultMap;
        }
        if (DictConstant.OPTION_DIRECT.PUT.equals(calcInstrumentForm.getOptionType())  && calcInstrumentForm.getTriggerRate().compareTo(new BigDecimal(calcInstrumentForm.getStrikePercentage()))!=-1) {
            throw new ServiceException(SummitTranConstants.TRIGGER_LESS_ERR);
//            resultMap.put("OK",false);
//            resultMap.put("msg",trade.getiCode()+"障碍价必须小于行权价,请核对!");
//            return resultMap;
        }
        // 校验到期支付偏移不能为负数
        if (calcInstrumentForm.getPaymentDaysOffset() < 0.0) {
            throw new ServiceException(SummitMsgBuilder.BuilderMsg(SummitTranConstants.PAYMENTDAYSOFFSET_WRONG_ERR, 10));
        }
        return resultMap;
    }

    public Map validDoubleShark(Trade trade){
        Map resultMap = new HashMap();
        //todo 对双鲨期权类型的Trade进行校验
        return resultMap;
    }

    public Boolean checkICodeExist(Trade trade){
        Map param = new HashMap();
        param.put("iCode", trade.getiCode());
        param.put("aType", trade.getaType());
        param.put("mType", trade.getmType());
        if (tradeMapper.querySingleTradeContract(param).size() > 0) {
           return false;
        }
        return true;
    }

    private BigDecimal getAnnualRatio(String calcDate, String begDate, String endDate, String dayCount) {
        long term = DateUtil.betweenDay(DateUtil.parseDate(begDate), DateUtil.parseDate(endDate), true);
        //365F 时需要判断是否闰年
        Integer daycouterDays = 365;
        if (dayCount.contains("F")) {
            daycouterDays = DateUtil.isLeapYear(Integer.parseInt(calcDate.substring(0, 4))) ? 366 : 365;
        } else {
            String regEx = "\\D+";
            daycouterDays = Integer.parseInt(dayCount.replaceAll(regEx, "").trim());
        }
        //天数默认加一天
        return NumberUtil.div(BigDecimal.valueOf(term), daycouterDays, 20);
    }

}
